package org.hdl.anima.fronent;

import java.net.InetSocketAddress;

import org.hdl.anima.AppConf;
import org.hdl.anima.Application;
import org.hdl.anima.blacklist.BlackListMgr;
import org.hdl.anima.common.module.BasicModule;
import org.hdl.anima.handler.ServiceHandlers;
import org.hdl.anima.message.ErrorResponse;
import org.hdl.anima.message.HandShakeReq;
import org.hdl.anima.message.HandShakeResp;
import org.hdl.anima.message.HandSnakeResp;
import org.hdl.anima.message.IMessage;
import org.hdl.anima.message.Response;
import org.hdl.anima.proxy.ServerProxys;
import org.hdl.anima.session.ClientSession;
import org.hdl.anima.session.ClientSessionMgr;
import org.hdl.anima.session.ISession;
import org.hdl.anima.session.TokenFactory;
import org.hdl.anima.transport.Channel;
import org.hdl.anima.transport.Constants;
import org.hdl.anima.transport.RemoteException;
import org.hdl.anima.transport.Transporters;
import org.hdl.anima.transport.support.AbstractServer;
import org.hdl.anima.transport.support.ChannelHandlerAdapter;
import org.hdl.anima.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author qiuhd
 * @since  2014-8-1
 * @version V1.0.0
 */
public class FronetServer extends BasicModule {
	
	private static final Logger logger = LoggerFactory.getLogger(FronetServer.class);
	private AbstractServer server ;
	private AppConf conf ;
	private Application application;
	private BlackListMgr blackListMgr;
	private ServiceHandlers handlers ;
	private ClientSessionMgr clientSessionMgr;
	private ServerProxys serverProxys;
	
	private ChannelHandlerAdapter handler = new ChannelHandlerAdapter(){
		@Override
		public void connected(Channel channel) throws RemoteException {
			InetSocketAddress socketAddress = channel.getRemoteAddress();
			String address = socketAddress.getHostString();
			boolean result = blackListMgr.contains(address);
			if (result) {
				//TODO 通知客户端
				//关闭客户端
				channel.close();
			}
			clientSessionMgr.createSession(channel);
			//TODO 需考虑是否通知用户
		}
		
		@Override
		public void sent(Channel channel, Object message) throws RemoteException {
			ISession session = ClientSessionMgr.getClientSession(channel);
//			if (message instanceof IRequest) {
//				IRequest request = (IRequest)message;
//				try {
//					afterFilterChain.doFilter(session, request, null);
//				} catch (Exception e) {
//					logger.error("Failed to execute after filter,cause:{}",e.getMessage(),e);
//				}
//			}
		}
		
		@Override
		public void caught(Channel channel, Throwable cause) throws RemoteException {
			super.caught(channel, cause);
			logger.error("Caugth exception :" + cause.getMessage(),cause);
		}

		@Override
		public void disconnected(Channel channel) throws RemoteException {
			logger.debug("Channel diconnect");
			ClientSession clientSession = ClientSessionMgr.getClientSession(channel);
			if (clientSession != null) {
				clientSession.close();
				//通知所有后端服务器关闭 BackenSession
				serverProxys.clientSessionClosed(clientSession.getIdentity());
			}
		}

		@Override
		public void received(Channel channel, Object message)throws RemoteException {
			ISession session = ClientSessionMgr.getClientSession(channel);
			if(session != null) {
				try {
					if (message instanceof HandShakeReq){
						handleHandSnakeReq((ClientSession)session,(HandShakeReq)message);
					}else {
						handlers.process(message, session);
					}
				} catch (Exception e) {
					logger.error(e.getMessage(),e);
				}
			}else {
				logger.warn("Remote client closed");
			}
		}
	};
	
	public FronetServer(String moduleName) {
		super(moduleName);
	}
	

	@Override
	public void initialize(Application application) {
		super.initialize(application);
		this.conf = application.getAppConf();
		this.application = application;
		this.blackListMgr = application.getMoulde(BlackListMgr.class);
		this.handlers = application.getMoulde(ServiceHandlers.class);
		this.clientSessionMgr = application.getMoulde(ClientSessionMgr.class);
		this.serverProxys = application.getMoulde(ServerProxys.class);
	}

	@Override
	public void start() throws IllegalStateException {
		try {
			FronentCodec codec = new FronentCodec(this.application);
			server = Transporters.bind(conf, handler,codec);
		} catch (RemoteException e) {
			throw new IllegalStateException("Failed to start the fronent server,cause:"+ e.getMessage(),e);
		}
	}

	@Override
	public void stop() {
		if (server != null) {
			server.close();
		}
	}

	@Override
	public void destroy() {
		stop();
		server = null;
	}
	
	/**
	 * @param session
	 * @param req
	 * @throws RemoteException 
	 */
	private void handleHandSnakeReq(ClientSession session,HandShakeReq req) throws RemoteException {
		String appVersion = this.application.getVersion().getVersionString();
		if (!req.getVersion().equals(appVersion)) {
			Response response = new ErrorResponse(4,"version error");
			session.send(response);
			return ;
		}
		session.setClientType(req.getClientType());
		
		int heartbeat = application.getAppConf().getInt(Constants.HEARTBEAT_TIME_KEY, Constants.DEFAULT_HEARTBEAT);
		int payload  = application.getAppConf().getInt(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);
		                    
		String reconnectToken = req.getReconnectToken();
	
		if (StringUtils.isEmpty(reconnectToken)) {
			//通知所有后端服务器创建 BackenSession
			InetSocketAddress remoteAddress = session.getRemoteAddress();
			InetSocketAddress localAddress = session.getLocalAddress();
			int identity = session.getIdentity();
			//通知所有后端服务器创建 BackenSession
			serverProxys.clientSessionCreated(identity,remoteAddress.getHostString(), remoteAddress.getPort(),
					localAddress.getHostString(), localAddress.getPort(),req.getClientType());
			reconnectToken = TokenFactory.getInstance().getUniqueSessionToken(session.getRemoteAddress().toString());
			HandSnakeResp handSnakeResp = new HandSnakeResp();
			handSnakeResp.setHeartbeatTime(heartbeat);
			handSnakeResp.setReconnectToken(reconnectToken);
			handSnakeResp.setPayload(payload);
			session.send(handSnakeResp);
		}else {	//实现断线重连
			
		}
	}
}

